var pitchShifter = {};
pitchShifter.hannWindow = function(length) {
	var window = new Float32Array(length);
	for (var i = 0; i < length; i++) {
		window[i] = 0.5 * (1 - Math.cos(2 * Math.PI * i / (length - 1)));
	}
	return window;
};
pitchShifter.linearInterpolation = function (a, b, t) {
    return a + (b - a) * t;
};
pitchShifter.grainSize = 1024;
pitchShifter.createNode = function(ctx) {
	var pitchShifterProcessor = null;
	if (ctx.createScriptProcessor) {
		pitchShifterProcessor = ctx.createScriptProcessor(pitchShifter.grainSize, 1, 1);
	} else if (ctx.createJavaScriptNode) {
		pitchShifterProcessor = ctx.createJavaScriptNode(pitchShifter.grainSize, 1, 1);
	}

	pitchShifterProcessor.buffer = new Float32Array(pitchShifter.grainSize * 2);
	pitchShifterProcessor.grainWindow = pitchShifter.hannWindow(pitchShifter.grainSize);
	pitchShifterProcessor.pitchRatio = 1.0;
	pitchShifterProcessor.overlapRatio = 0.5;
	pitchShifterProcessor.onaudioprocess = function(event) {

		var inputData = event.inputBuffer.getChannelData(0);
		var outputData = event.outputBuffer.getChannelData(0);

		for (i = 0; i < inputData.length; i++) {

			// Apply the window to the input buffer
			inputData[i] *= pitchShifterProcessor.grainWindow[i];

			// Shift half of the buffer
			pitchShifterProcessor.buffer[i] = pitchShifterProcessor.buffer[i + pitchShifter.grainSize];

			// Empty the buffer tail
			pitchShifterProcessor.buffer[i + pitchShifter.grainSize] = 0.0;
		}

		// Calculate the pitch shifted grain re-sampling and looping the input
		var grainData = new Float32Array(pitchShifter.grainSize * 2);
		for (var i = 0, j = 0.0; i < pitchShifter.grainSize; i++, j += pitchShifterProcessor.pitchRatio) {

			var index = Math.floor(j) % pitchShifter.grainSize;
			var a = inputData[index];
			var b = inputData[(index + 1) % pitchShifter.grainSize];
			grainData[i] += pitchShifter.linearInterpolation(a, b, j % 1.0) * pitchShifterProcessor.grainWindow[i];
		}

		// Copy the grain multiple times overlapping it
		for (i = 0; i < pitchShifter.grainSize; i += Math.round(pitchShifter.grainSize * (1 - pitchShifterProcessor.overlapRatio))) {
			for (j = 0; j <= pitchShifter.grainSize; j++) {
				pitchShifterProcessor.buffer[i + j] += grainData[j];
			}
		}

		// Output the first half of the buffer
		for (i = 0; i < pitchShifter.grainSize; i++) {
			outputData[i] = pitchShifterProcessor.buffer[i];
		}
	};
	return pitchShifterProcessor;
};